/**
 * 创建并返回一个基于浏览器 hash 的自定义历史对象
 */
function createHashHistory() {
  let stack = [] // 定义历史条目栈
  let index = -1; // 历史栈指针索引
  let action = 'POP' // 定义当前的动作
  let state = null // 状态
  let listeners = [] // 定义监听函数的数组

  /**
   * 当浏览器地址变化时 会自动执行数组中所有的监听函数
   */
  function listen(listener) {
    listeners.push(listener) // 保存监听路径变化需要执行的函数
    return () => { // 返回一个取消订阅的函数
      listeners = listeners.filter(item => item !== listener) // 移除监听函数
    }
  }

  /**
   * 添加一个新的路径 ( 历史记录条目 ) 和状态的函数\
   * 两种使用方法 push('/user', {id: 1}) 或者 push({pathname: "/user", state: {id: 1}})
   */
  function push(pathname, nextState) {
    action = 'PUSH' // 定义动作为 PUSH 
    if (typeof pathname === 'object') { // 兼容对象形式调用
      state = pathname.state
      pathname = pathname
    } else { // 字符串形式调用
      state = nextState
    }
    // 通过修改 hash 来修改路径
    window.location.hash = pathname
  }

  function go(n) {
    action = 'POP' // 动作类型
    index += n // 修改历史记录栈的指针
    let nextLocation = stack[index] // 再从历史栈中获取对应的 location
    state = nextLocation.state // 从 location 中获取 state 状态
    window.location.hash = nextLocation.pathname // 修改浏览器地址栏中 hash 值
  }

  function goBack() {
    history.go(-1)
  }

  function goForward() {
    history.go(1)
  }

  /**
   * 当浏览器地址栏中 hash 部分发生变化自动执行 hashchange 事件
   */
  function handleHashChange() {
    // 获取最新的写在 hash 值中的路径名
    const pathname = window.location.hash.slice(1) // 获取 hash # 号后面的值
    history.action = action // 修改当前自定义 hash 历史对象的 action 动作属性
    const location = { pathname, action } // 实现一个自定义的 location 对象
    history.location = location
    if (action === 'PUSH') {
      stack[++index] = location // 往历史栈中新增一个条目
    }

    // 依次遍历所有的监听函数
    listeners.forEach(listener => {
      listener({ action, location }) // 并在执行每个监听函数的时候传递 { action, location }
    })
  }

  window.addEventListener('hashchange', handleHashChange)

  // 初始化自定义的历史对象
  const history = {
    action: 'POP', // 动作
    go,
    goBack,
    goForward,
    push,
    listen,
    location: {
      pathname: undefined, // 路径
      state: undefined, // 路径对应的状态 可能没有 调用了原生 pushState 将传递状态才有
    }
  }

  // 初始化 hash 路径
  if(window.location.hash) { // 有 hash 值的时候
    action = 'PUSH' // 将状态修改为 PUSH
    handleHashChange() // 往历史条目中添加一条记录
  } else {
    window.location.hash = '/' // 初始化 hash 为 /
  }

  return history
}

export default createHashHistory
